#include "utilc_string.h"
#include <string.h>
#include <ctype.h>

static void make_capacity(util_string_t* pstr_string, size_t t_len) {
    size_t new_capacity = pstr_string->capacity;
    if(new_capacity == 0)
        new_capacity = 8;
    while(true) {
        if(new_capacity > t_len)
            break;
        new_capacity *= 2;
    }
    if(new_capacity > pstr_string->capacity) {
        if(pstr_string->value == NULL)
            pstr_string->value = (char*)calloc(pstr_string->elemsize, new_capacity);
        else
            pstr_string->value = (char*)realloc(pstr_string->value, new_capacity*pstr_string->elemsize);
        pstr_string->capacity = new_capacity;
    }
}

util_string_t* util_string_create() {
    SAFE_MALLOC(util_string_t, ret);
    ret->elemsize = sizeof(char);
    return ret;
}

void util_string_ref(util_string_t* pstr_string) {
    pstr_string->refcount++;
}

void util_string_unref(util_string_t* pstr_string) {
    if(pstr_string->refcount == 0) {
        util_string_destroy(pstr_string);
        return;
    }
    pstr_string->refcount--;
}

void util_string_init(util_string_t* pstr_string) {
    if(pstr_string->value != NULL)
        return;
    make_capacity(pstr_string, 1);
}

void util_string_init_char(util_string_t* pstr_string, size_t t_count, char c_char) {
    make_capacity(pstr_string, t_count + 1);
    memset(pstr_string->value, c_char, t_count);
    pstr_string->length = t_count;
}

void util_string_init_cstr(util_string_t* pstr_string, const char* s_cstr) {
    size_t t_count = strlen(s_cstr);
    make_capacity(pstr_string, t_count + 1);
    memcpy(pstr_string->value, s_cstr, t_count);
    pstr_string->length = t_count;
}

void util_string_init_subcstr(util_string_t* pstr_string, const char* s_cstr, size_t t_len) {
    pstr_string->refcount = 1;
    make_capacity(pstr_string, t_len + 1);
    memcpy(pstr_string->value, s_cstr, t_len);
    pstr_string->length = t_len;
}

void util_string_init_copy(util_string_t* pstr_dest, const util_string_t* cpstr_src) {
    size_t t_count = util_string_size(cpstr_src);
    make_capacity(pstr_dest, t_count + 1);
    memcpy(pstr_dest->value, util_string_c_str(cpstr_src), t_count);
    pstr_dest->length = t_count;
}

void util_string_init_copy_substring(util_string_t* pstr_dest, const util_string_t* cpstr_src, size_t t_pos, size_t t_len) {
    make_capacity(pstr_dest, t_len + 1);
    memcpy(pstr_dest->value, util_string_c_str(cpstr_src)+t_pos, t_len);
    pstr_dest->length = t_len;
}

void util_string_destroy(util_string_t* pstr_string) {
    free(pstr_string->value);
    free(pstr_string);
}

const char* util_string_c_str(const util_string_t* cpstr_string) {
    return (const char*)cpstr_string->value;
}

size_t util_string_copy(const util_string_t* cpstr_src, char* s_dest, size_t t_len, size_t t_pos) {
    size_t t_count = util_string_size(cpstr_src);
    if(t_count < t_pos)
        return -1;
    t_count -= t_pos;
    if(t_count > t_len)
        t_count = t_len;

    memcpy(s_dest, cpstr_src->value + t_pos, t_count);

    return t_count;
}

size_t util_string_size(const util_string_t* cpstr_string) {
    return cpstr_string->length;
}

bool util_string_empty(const util_string_t* cpstr_string) {
    return cpstr_string->length == 0;
}

char util_string_at(const util_string_t* cpstr_string, size_t t_pos) {
    return cpstr_string->value[t_pos];
}

bool util_string_equal(const util_string_t* cpstr_first, const util_string_t* cpstr_second) {
    if(cpstr_first->length != cpstr_second->length)
        return false;

    if(memcmp(cpstr_first->value, cpstr_second->value, cpstr_second->length))
        return false;

    return true;
}

bool util_string_equal_cstr(const util_string_t* cpstr_string, const char* s_cstr) {
    size_t t_count = strlen(s_cstr);
    if(cpstr_string->length != t_count)
        return false;

    if(memcmp(cpstr_string->value, s_cstr, t_count))
        return false;

    return true;
}

int util_string_compare(const util_string_t* cpstr_first, const util_string_t* cpstr_second) {
    size_t t_count = cpstr_first->length < cpstr_second->length ? cpstr_first->length : cpstr_second->length;
    int ret = memcmp(cpstr_first->value, cpstr_second->value, t_count);
    if(ret)
        return ret;

    if(cpstr_first->length < cpstr_second->length)
        return -cpstr_first->length;
    if(cpstr_first->length > cpstr_second->length)
        return cpstr_second->length;
    return 0;
}

int util_string_compare_substring_string( const util_string_t* cpstr_first, size_t t_firstpos, size_t t_firstlen, const util_string_t* cpstr_second) {
    size_t t_count = t_firstlen < cpstr_second->length ? t_firstlen : cpstr_second->length;
    int ret = memcmp(cpstr_first->value + t_firstpos, cpstr_second->value, t_count);
    if(ret)
        return ret;

    if(t_firstlen < cpstr_second->length)
        return -t_firstlen;
    if(t_firstlen > cpstr_second->length)
        return cpstr_second->length;
    return 0;
}

int util_string_compare_substring_substring(
    const util_string_t* cpstr_first, size_t t_firstpos, size_t t_firstlen,
    const util_string_t* cpstr_second, size_t t_secondpos, size_t t_secondlen) {
        size_t t_count = t_firstlen < t_secondlen ? t_firstlen : t_secondlen;
        int ret = memcmp(cpstr_first->value + t_firstpos, cpstr_second->value + t_secondpos, t_count);
        if(ret)
            return ret;

        if(t_firstlen < t_secondlen)
            return -t_firstlen;
        if(t_firstlen > t_secondlen)
            return t_secondlen;
        return 0;
}

int util_string_compare_cstr(const util_string_t* cpstr_string, const char* s_cstr) {
    size_t t_firstlen = cpstr_string->length;
    size_t t_secondlen = strlen(s_cstr);
    size_t t_count = t_firstlen < t_secondlen ? t_firstlen : t_secondlen;
    int ret = memcmp(cpstr_string->value, s_cstr, t_count);
    if(ret)
        return ret;

    if(t_firstlen < t_secondlen)
        return -t_firstlen;
    if(t_firstlen > t_secondlen)
        return t_secondlen;
    return 0;
}

int util_string_compare_substring_cstr(
    const util_string_t* cpstr_string, size_t t_stringpos, size_t t_stringlen, const char* s_cstr) {
        size_t t_secondlen = strlen(s_cstr);
        size_t t_count = t_stringlen < t_secondlen ? t_stringlen : t_secondlen;
        int ret = memcmp(cpstr_string->value + t_stringpos, s_cstr, t_count);
        if(ret)
            return ret;

        if(t_stringlen < t_secondlen)
            return -t_stringlen;
        if(t_stringlen > t_secondlen)
            return t_secondlen;
        return 0;
}

int util_string_compare_substring_subcstr(
    const util_string_t* cpstr_string, size_t t_stringpos, size_t t_stringlen, const char* s_cstr, size_t t_len) {
        size_t t_count = t_stringlen < t_len ? t_stringlen : t_len;
        int ret = memcmp(cpstr_string->value + t_stringpos, s_cstr, t_count);
        if(ret)
            return ret;

        if(t_stringlen < t_len)
            return -t_stringlen;
        if(t_stringlen > t_len)
            return t_len;
        return 0;
}

util_string_t* util_string_substr(const util_string_t* cpstr_string, size_t t_pos, size_t t_len) {
    util_string_t* ret = util_string_create();
    util_string_init_copy_substring(ret, cpstr_string, t_pos, t_len);
    return ret;
}

size_t util_string_find(const util_string_t* cpstr_string, const util_string_t* cpstr_find, size_t t_pos) {
    if (util_string_empty(cpstr_find)) {
        return t_pos <= util_string_size(cpstr_string) ? t_pos : UTIL_STRING_NPOS;
    }

    if (util_string_size(cpstr_find) <= util_string_size(cpstr_string)) {
        for (; t_pos <= util_string_size(cpstr_string) - util_string_size(cpstr_find); ++t_pos) {
            if (util_string_compare_substring_substring(
                cpstr_string, t_pos, util_string_size(cpstr_find),
                cpstr_find, 0, util_string_size(cpstr_find)) == 0) {
                    return t_pos;
            }
        }
    }

    return UTIL_STRING_NPOS;
}

size_t util_string_find_cstr(const util_string_t* cpstr_string, const char* s_cstr, size_t t_pos) {
    return util_string_find_subcstr( cpstr_string, s_cstr, t_pos, strlen(s_cstr));
}

size_t util_string_find_subcstr(const util_string_t* cpstr_string, const char* s_cstr, size_t t_pos, size_t t_len) {
    if (t_len == 0) {
        return t_pos <= util_string_size(cpstr_string) ? t_pos : UTIL_STRING_NPOS;
    }

    if (t_len <= util_string_size(cpstr_string)) {
        for (; t_pos <= util_string_size(cpstr_string) - t_len; ++t_pos) {
            if (util_string_compare_substring_subcstr(cpstr_string, t_pos, t_len, s_cstr, t_len) == 0) {
                return t_pos;
            }
        }
    }

    return UTIL_STRING_NPOS;
}

size_t util_string_find_char(const util_string_t* cpstr_string, char c_char, size_t t_pos) {
    for (; t_pos <= util_string_size(cpstr_string); ++t_pos) {
        if (util_string_at(cpstr_string, t_pos) == c_char) {
            return t_pos;
        }
    }

    return UTIL_STRING_NPOS;
}

size_t util_string_rfind(const util_string_t* cpstr_string, const util_string_t* cpstr_find, size_t t_pos) {
    size_t t_len = util_string_size(cpstr_find);
    if (t_len <= util_string_size(cpstr_string)) {
        t_pos = t_pos < util_string_size(cpstr_string) - t_len ? t_pos : util_string_size(cpstr_string) - t_len;

        do {
            if (util_string_compare_substring_substring(cpstr_string, t_pos, t_len, cpstr_find, 0, t_len) == 0) {
                return t_pos;
            }
        } while (t_pos-- > 0);
    }

    return UTIL_STRING_NPOS;
}

size_t util_string_rfind_cstr(const util_string_t* cpstr_string, const char* s_cstr, size_t t_pos) {
    return util_string_rfind_subcstr(cpstr_string, s_cstr, 0, t_pos);
}

size_t util_string_rfind_subcstr(const util_string_t* cpstr_string, const char* s_cstr, size_t t_pos, size_t t_len) {
    if (t_len <= util_string_size(cpstr_string)) {
        t_pos = t_pos < util_string_size(cpstr_string) - t_len ? t_pos : util_string_size(cpstr_string) - t_len;

        do {
            if (util_string_compare_substring_subcstr(cpstr_string, t_pos, t_len, s_cstr, t_len) == 0) {
                return t_pos;
            }
        } while (t_pos-- > 0);
    }

    return UTIL_STRING_NPOS;
}

size_t util_string_rfind_char(const util_string_t* cpstr_string, char c_char, size_t t_pos) {
    return util_string_rfind_subcstr(cpstr_string, &c_char, t_pos, 1);
}

void util_string_clear(util_string_t* pstr_string) {
    pstr_string->value[0] = 0;
    pstr_string->length = 0;
}

void util_string_push_back(util_string_t* pstr_string, char c_char) {
    make_capacity(pstr_string, pstr_string->length + 1);
    pstr_string->value[pstr_string->length] = c_char;
    pstr_string->value[pstr_string->length+1] = 0;
    pstr_string->length++;
}

void util_string_append(util_string_t* pstr_dest, const util_string_t* cpstr_src) {
    make_capacity(pstr_dest, pstr_dest->length + cpstr_src->length);
    memcpy(pstr_dest->value + pstr_dest->length, cpstr_src->value, cpstr_src->length);
    pstr_dest->length += cpstr_src->length;
    pstr_dest->value[pstr_dest->length] = 0;
}

void util_string_append_substring(util_string_t* pstr_dest, const util_string_t* cpstr_src, size_t t_pos, size_t t_len) {
    make_capacity(pstr_dest, pstr_dest->length + t_len);
    memcpy(pstr_dest->value + pstr_dest->length, cpstr_src->value + t_pos, t_len);
    pstr_dest->length += t_len;
    pstr_dest->value[pstr_dest->length] = 0;
}

void util_string_append_cstr(util_string_t* pstr_dest, const char* s_cstr) {
    size_t t_len = strlen(s_cstr);
    make_capacity(pstr_dest, pstr_dest->length + t_len);
    memcpy(pstr_dest->value + pstr_dest->length, s_cstr, t_len);
    pstr_dest->length += t_len;
    pstr_dest->value[pstr_dest->length] = 0;
}

void util_string_append_subcstr(util_string_t* pstr_dest, const char* s_cstr, size_t t_len) {
    make_capacity(pstr_dest, pstr_dest->length + t_len);
    memcpy(pstr_dest->value + pstr_dest->length, s_cstr, t_len);
    pstr_dest->length += t_len;
    pstr_dest->value[pstr_dest->length] = 0;
}
void util_string_append_char(util_string_t* pstr_dest, size_t t_count, char c_char) {
    make_capacity(pstr_dest, pstr_dest->length + t_count);
    memset(pstr_dest->value + pstr_dest->length, c_char, t_count);
    pstr_dest->length += t_count;
    pstr_dest->value[pstr_dest->length] = 0;
}

void util_string_insert_string(util_string_t* pstr_string, size_t t_pos, const util_string_t* cpstr_insert) {
    make_capacity(pstr_string, pstr_string->length + cpstr_insert->length);
    memcpy(pstr_string->value + t_pos + cpstr_insert->length, pstr_string->value + t_pos, cpstr_insert->length);
    memcpy(pstr_string->value + t_pos, cpstr_insert->value, cpstr_insert->length);
    pstr_string->length += cpstr_insert->length;
    pstr_string->value[pstr_string->length] = 0;
}

void util_string_insert_substring(util_string_t* pstr_string, size_t t_pos, const util_string_t* cpstr_insert, size_t t_startpos, size_t t_len) {
    make_capacity(pstr_string, pstr_string->length + t_len);
    memcpy(pstr_string->value + t_pos + t_len, pstr_string->value + t_pos, t_len);
    memcpy(pstr_string->value + t_pos, cpstr_insert->value + t_startpos, t_len);
    pstr_string->length += t_len;
    pstr_string->value[pstr_string->length] = 0;
}

void util_string_insert_cstr(util_string_t* pstr_string, size_t t_pos, const char* s_cstr) {
    size_t t_len = strlen(s_cstr);
    util_string_insert_subcstr(pstr_string, t_pos, s_cstr, t_len);
}

void util_string_insert_subcstr(util_string_t* pstr_string, size_t t_pos, const char* s_cstr, size_t t_len) {
    make_capacity(pstr_string, pstr_string->length + t_len);
    memcpy(pstr_string->value + t_pos + t_len, pstr_string->value + t_pos, t_len);
    memcpy(pstr_string->value + t_pos, s_cstr, t_len);
    pstr_string->length += t_len;
    pstr_string->value[pstr_string->length] = 0;
}

void util_string_insert_char(util_string_t* pstr_string, size_t t_pos, size_t t_count, char c_char) {
    make_capacity(pstr_string, pstr_string->length + t_count);
    memcpy(pstr_string->value + t_pos + t_count, pstr_string->value + t_pos, t_count);
    memset(pstr_string->value + t_pos, c_char, t_count);
    pstr_string->length += t_count;
    pstr_string->value[pstr_string->length] = 0;
}

void util_string_erase_substring(util_string_t* pstr_string, size_t t_pos, size_t t_len) {
    size_t move_len, i;
    if(t_pos + t_len > pstr_string->length)
        t_len = pstr_string->length - t_pos;
    move_len = pstr_string->length - t_pos - t_len;

    for(i=0; i<move_len; ++i) {
        pstr_string->value[t_pos + i] = pstr_string->value[t_pos + t_len + i];
    }
    pstr_string->value[t_pos + t_len] = 0;
}

void util_string_replace(util_string_t* pstr_string, size_t t_pos, size_t t_len, const util_string_t* cpstr_replace) {
    util_string_erase_substring(pstr_string, t_pos, t_len);
    util_string_insert_string(pstr_string, t_pos, cpstr_replace);
}

void util_string_replace_substring(util_string_t* pstr_string, size_t t_pos, size_t t_len, const util_string_t* cpstr_replace, size_t t_position, size_t t_length) {
    util_string_erase_substring(pstr_string, t_pos, t_len);
    util_string_insert_substring(pstr_string, t_pos, cpstr_replace, t_position, t_length);
}

void util_string_replace_cstr(util_string_t* pstr_string, size_t t_pos, size_t t_len, const char* s_cstr) {
    util_string_erase_substring(pstr_string, t_pos, t_len);
    util_string_insert_cstr(pstr_string, t_pos, s_cstr);
}

void util_string_replace_subcstr(util_string_t* pstr_string, size_t t_pos, size_t t_len, const char* s_cstr, size_t t_length) {
    util_string_erase_substring(pstr_string, t_pos, t_len);
    util_string_insert_subcstr(pstr_string, t_pos, s_cstr, t_length);
}

void util_string_replace_char(util_string_t* pstr_string, size_t t_pos, size_t t_len, size_t t_count, char c_char) {
    util_string_erase_substring(pstr_string, t_pos, t_len);
    util_string_insert_char(pstr_string, t_pos, t_count, c_char);
}

//////////////////////////////////////////////////////////////////////////

char* utilc_str_copy(char* src) {
    char *dst = (char*)calloc(1, strlen(src)+1);
    if(dst == NULL)
        return dst;
    strcpy(dst, src);
    return dst;
}

char* utilc_mem_copy(char* src, int size) {
    char *dst = (char*)malloc(size);
    if(dst == NULL)
        return dst;
    memcpy(dst, src, size);
    return dst;
}

int htoi(const char *s) {
    int i;
    int ret = 0;
    int len = strlen(s);
    for (i = 0; i<len; ++i) {
        if (s[i] >= 'a' && s[i] <= 'f') {
            ret = 16 * ret + (10 + s[i] - 'a');
        } else if (s[i] >= 'A' && s[i] <= 'F') {
            ret = 16 * ret + (10 + s[i] - 'A');
        } else if (s[i] >= '0' && s[i] <= '9') {
            ret = 16 * ret + (s[i] - '0');
        } 
    }
    return ret;
}